package com.lagou.edu.factory;

import com.lagou.edu.annotation.Autowired;
import com.lagou.edu.annotation.Service;
import com.lagou.edu.annotation.Transactional;
import com.lagou.edu.utils.PacketScannerUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

public class AnnotationBeanFactory {


    /**
     * 单例池
     */
    private static Map<String,Object> singletonObjects = new HashMap<>();


    /**
     * 二级缓存
     */
    private static Map<String,Object> earlyObjects = new HashMap<>();



    static  {
        PacketScannerUtils packetScannerUtils = new PacketScannerUtils();
        try {
            //扫描包
            packetScannerUtils.scanPacket("com.lagou");
            Set<Class<?>> classSet = packetScannerUtils.getClassSet();

            //实例化bean
            for (Class<?> klass : classSet) {
                Service annotation = klass.getAnnotation(Service.class);
                if(annotation!=null){
                    String beanName = annotation.value();
                    System.out.println(beanName);
                    if("".equals(beanName)){
                        beanName = klass.getName();
                    }
                    earlyObjects.put(beanName,klass.newInstance());
                }
            }

            //设置依赖
            for(String beanName:earlyObjects.keySet()){
                Object bean = earlyObjects.get(beanName);
                Class<?> beanClass = bean.getClass();
                Field[] fields = beanClass.getDeclaredFields();
                for (Field field : fields) {
                    Autowired autowired = field.getAnnotation(Autowired.class);
                    if(autowired != null){
                        Object obj = findBean(field.getType());
                        if(obj==null){
                            throw new RuntimeException("未找到bean，class："+field.getType());
                        }
                        field.setAccessible(true);
                        field.set(bean,obj);
                    }
                }
                //触发一次，将未放入单例池的bean放入到单例池中
                upgradeBean(beanName);
            }
            earlyObjects.clear();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 根据id获取bean
     * @param id
     * @return
     */
    public static  Object getBean(String id) {
        return singletonObjects.get(id);
    }

    /**
     * 根据类型获取bean
     * @param clazz
     * @return
     */
    public static Object getBean(Class clazz){
        for (Object obj : singletonObjects.values()){
           if(clazz.isAssignableFrom(obj.getClass())) {
               return obj;
           }
        }
        return null;
    }

    /**
     * 从单例池中获取bean，如果单例池中没有发现则从二级缓存中获取
     * @param clazz
     * @return
     */
    private static Object findBean(Class clazz){
        for (Object obj : singletonObjects.values()){
            if(clazz.isAssignableFrom(obj.getClass())) {
                return obj;
            }
        }
        for(String beanName:earlyObjects.keySet()){
            Object obj = earlyObjects.get(beanName);
            if(obj !=null && clazz.isAssignableFrom(obj.getClass())) {
                //判断是否需要代理，升级bean并返回
                return upgradeBean(beanName);
            }
        }
        throw new RuntimeException("未找到bean，class为"+ clazz);
    }

    /**
     * 升级bean，将bean从二级缓存升级到单例池中，如果bean需要代理对象在升级过程中生成
     * @param beanName
     * @return
     */
    private static Object upgradeBean(String beanName){
        //如果在单例池中发现bean则直接返回
        Object singletonBean = singletonObjects.get(beanName);
        if(singletonBean!=null){
            return singletonBean;
        }
        Object bean = earlyObjects.get(beanName);
        if(bean != null){
            //设置代理
            boolean jkdProxy = false;
            boolean cglibProxy = false;
            Class<?>[] interfaces = bean.getClass().getInterfaces();
            abc: for(Class interfaceClass:interfaces){
                if(interfaceClass.getAnnotation(Transactional.class) !=null){
                    jkdProxy = true;
                    break;
                };

                Method[] methods1 = interfaceClass.getMethods();
                for (Method method : methods1) {
                    if(method.getAnnotation(Transactional.class) !=null){
                        jkdProxy = true;
                        break abc;
                    }
                }
            }
            if(jkdProxy==false){
                if(bean.getClass().getAnnotation(Transactional.class)!=null){
                    cglibProxy = true;
                }else{
                    Method[] methods = bean.getClass().getMethods();
                    for(Method method:methods){
                        if( method.getAnnotation(Transactional.class) != null){
                            cglibProxy = true;
                            break;
                        }
                    }
                }
            }
            Object singletonObject;
            if(jkdProxy==true){
                ProxyFactory proxyFactory = (ProxyFactory) findBean(ProxyFactory.class);
                singletonObject = proxyFactory.getJdkProxy(bean);
            }else if(cglibProxy == true){
                ProxyFactory proxyFactory = (ProxyFactory) findBean(ProxyFactory.class);
                singletonObject = proxyFactory.getCglibProxy(bean);
            }else{
                singletonObject = bean;
            }
            //升级bean到单例池
            singletonObjects.put(beanName,singletonObject);
            return singletonObject;
        }
        return null;
    }


}
